﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Shapes;
using System.Windows.Media;
using WPFDiagramming.Interfaces;
using WPFDiagramming.Controls;

namespace WPFDiagramming.Adorners
{
    public class DragAndDropAdorner : Adorner
    {
        private VisualCollection visualChildren = null;

        private List<Point> _offset;
        private bool _isDragging;

        private Rectangle rectangle;

        public DragAndDropAdorner(IDraggable element)
            : base((UIElement)element)
        {
            this.visualChildren = new VisualCollection(this);

            this.BuildRectangle(ref this.rectangle);            
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            this.rectangle.Arrange(new Rect(finalSize));

            return finalSize;
        }

        protected override Size MeasureOverride(Size constraint)
        {
            this.rectangle.Measure(constraint);
            
            return this.rectangle.DesiredSize;
        }

        private void BuildRectangle(ref Rectangle r)
        {
            r = new Rectangle();

            r.StrokeThickness = 3;

            r.RadiusX = 5;
            r.RadiusY = 5;

            r.Stroke = Brushes.Black;
            r.Cursor = Cursors.ScrollAll;

            r.PreviewMouseDown += element_PreviewMouseDown;
            r.MouseMove += element_PreviewMouseMove;
            r.PreviewMouseUp += element_PreviewMouseUp;

            this.visualChildren.Add(this.rectangle);
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            this.rectangle.Width = (double)this.AdornedElement.GetValue(FrameworkElement.WidthProperty);
            this.rectangle.Height = (double)this.AdornedElement.GetValue(FrameworkElement.HeightProperty);

            this.rectangle.SetValue(Canvas.LeftProperty, (double)this.AdornedElement.GetValue(Canvas.LeftProperty));
            this.rectangle.SetValue(Canvas.TopProperty, (double)this.AdornedElement.GetValue(Canvas.TopProperty));

            base.OnRender(drawingContext);
        }

        private void element_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            IDraggable element = this.AdornedElement as IDraggable; 
            
            if (element == null) return;                     
            
            _isDragging = true;
            
            _offset = new List<Point>();

            DiagramPanel parentElement = element.Parent as DiagramPanel;

            parentElement.MouseMove += element_PreviewMouseMove;

            foreach (var item in parentElement.SelectedElements)
            {
                _offset.Add(e.GetPosition(item));
                this.InvalidateVisual();
            }
        }   

        private void element_PreviewMouseMove(object sender, MouseEventArgs e)    
        {
            if (!_isDragging) return;

            IDraggable element = this.AdornedElement as IDraggable;

            DiagramPanel parentElement = element.Parent as DiagramPanel;

            for (int i = 0; i < parentElement.SelectedElements.Count; i++)
            {
                if (parentElement.SelectedElements[i] is IDraggable)
                {
                    Point offset = _offset[i];
                    Point mousePoint = e.GetPosition(parentElement);
                    mousePoint.Offset(-offset.X, -offset.Y);

                    parentElement.SelectedElements[i].SetValue(Canvas.LeftProperty, mousePoint.X);
                    parentElement.SelectedElements[i].SetValue(Canvas.TopProperty, mousePoint.Y);
                }
            }            
        }    

        private void element_PreviewMouseUp(object sender, MouseButtonEventArgs e)     
        {     
            _isDragging = false;

            IDraggable element = this.AdornedElement as IDraggable;

            DiagramPanel parentElement = element.Parent as DiagramPanel;

            parentElement.MouseMove -= element_PreviewMouseMove;
        }

        protected override Visual GetVisualChild(int index)
        {
            return this.visualChildren[index];
        }

        protected override int VisualChildrenCount
        {
            get
            {
                return visualChildren.Count;
            }
        }
    }
}
